package track

import (
	"crypto/rand"
	"encoding/binary"
	"errors"
	"sync/atomic"
)

var UnImplemented = errors.New("un implemented")

// RTPMaxPayloadSize 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header) - 12(RTP header)
const RTPMaxPayloadSize = 1460

type CodecId string

const (
	CodecIdUnKnown CodecId = "UnKnownCodecId"
	CodecIdH264    CodecId = "H264"
	CodecIdH265    CodecId = "H265"
	CodecIdAV1     CodecId = "AV1"

	CodecIdAAC   CodecId = "MPEG4-GENERIC"
	CodecIdOPUS  CodecId = "OPUS"
	CodecIdG726  CodecId = "G726"
	CodecIdG711A CodecId = "PCMA"
	CodecIdG711U CodecId = "PCMU"

	CodecIdPS CodecId = "MP2P"
	CodecIdTS CodecId = "MP2T" // "MPEG-TS"

	CodecIdFile CodecId = "FILE"
	CodecIdTSP  CodecId = "TSP"
)

func (c CodecId) String() string {
	return string(c)
}

func (c CodecId) MediaType() MediaType {
	switch c {
	case CodecIdH264, CodecIdH265, CodecIdPS, CodecIdTS, CodecIdAV1:
		return MediaTypeVideo
	case CodecIdAAC, CodecIdOPUS, CodecIdG726, CodecIdG711A, CodecIdG711U:
		return MediaTypeAudio
	case CodecIdFile, CodecIdTSP:
		return MediaTypeApplication
	default:
		return MediaTypeApplication
	}
}

// Direction is a marker for transmission directon of an endpoint
type Direction int

const (
	DirectionSendRecv Direction = iota + 1
	DirectionSendOnly
	DirectionRecvOnly
)

func (d Direction) RecvOnly() bool {
	return d == DirectionRecvOnly
}

func (d Direction) HasSend() bool {
	return d != DirectionRecvOnly
}

func (d Direction) HasRecv() bool {
	return d != DirectionSendOnly
}

func (d Direction) SendOnly() bool {
	return d == DirectionSendOnly
}

func (d Direction) String() string {
	switch d {
	case DirectionSendRecv:
		return "sendrecv"
	case DirectionSendOnly:
		return "sendonly"
	case DirectionRecvOnly:
		return "recvonly"
	default:
		return "errorDir"
	}
}

type MediaType string

const (
	MediaTypeVideo       MediaType = "video"
	MediaTypeAudio       MediaType = "audio"
	MediaTypeApplication MediaType = "application"
)

func (v MediaType) String() string {
	return string(v)
}

func (v MediaType) Video() bool {
	return v == MediaTypeVideo
}

func (v MediaType) Audio() bool {
	return v == MediaTypeAudio
}

func (v MediaType) Application() bool {
	return v == MediaTypeApplication
}

var _ssrc atomic.Uint32

func NewSSRC() uint32 {
	if _ssrc.Load() == 0 {
		_ssrc.Store(100)
	}

	_ssrc.Add(1)
	return _ssrc.Load()
}

var _id atomic.Uint32

func NewID() int {
	return int(_id.Add(1))
}

func NewSessionID() (uint64, error) {
	// https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-26#section-5.2.1
	// Session ID is recommended to be constructed by generating a 64-bit
	// quantity with the highest bit set to zero and the remaining 63-bits
	// being cryptographically random.
	id, err := cryptoUint64()
	return id & (^(uint64(1) << 63)), err
}

// cryptoUint64 returns cryptographic random uint64.
func cryptoUint64() (uint64, error) {
	var v uint64
	if err := binary.Read(rand.Reader, binary.LittleEndian, &v); err != nil {
		return 0, err
	}
	return v, nil
}
